package vip.liux.backend.infrastructure.jpa.pay;

import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import vip.liux.contracts.models.pay.PayRefund;
import vip.liux.contracts.repositories.pay.PayRefundRepository;
import vip.liux.contracts.shared.enums.pay.PayRefundStatus;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public interface PayRefundJpaRepository extends JpaRepository<PayRefund, Long>, PayRefundRepository, JpaSpecificationExecutor<PayRefund> {

    @Override
    default Page<PayRefund> findPage(Long appId, String channelCode, String merchantOrderId, String merchantRefundId, String channelOrderNo, String channelRefundNo, PayRefundStatus status, LocalDateTime start, LocalDateTime end, Pageable page) {
        return findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (appId != null) {
                predicates.add(criteriaBuilder.equal(root.get("appId"), appId));
            }
            if (channelCode != null && !channelCode.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("channelCode"), channelCode));
            }
            if (merchantOrderId != null && !merchantOrderId.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("merchantOrderId"), merchantOrderId));
            }
            if (merchantRefundId != null && !merchantRefundId.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("merchantRefundId"), merchantRefundId));
            }
            if (channelOrderNo != null && !channelOrderNo.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("channelOrderNo"), channelOrderNo));
            }
            if (channelRefundNo != null && !channelRefundNo.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("channelRefundNo"), channelRefundNo));
            }
            if (status != null) {
                predicates.add(criteriaBuilder.equal(root.get("status"), status));
            }
            if (start != null && end != null) {
                predicates.add(criteriaBuilder.between(root.get("creationTime"), start, end));
            } else if (start != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("creationTime"), start));
            } else if (end != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("creationTime"), end));
            }
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        }, page);
    }

    @Modifying
    @Query("update PayRefund set channelRefundNo = :channelRefundNo, status = :updateStatus, " +
            "channelNotifyData = :channelNotifyData, channelErrorCode = :channelErrorCode, " +
            "channelErrorMsg = :channelErrorMsg where id = :id and status = :status")
    @Override
    int updateRefundFailure(Long id, PayRefundStatus status, String channelRefundNo, PayRefundStatus updateStatus, String channelNotifyData, String channelErrorCode, String channelErrorMsg);

    @Modifying
    @Query("update PayRefund set successTime = :successTime, channelRefundNo = :channelRefundNo, status = :updateStatus, " +
            "channelNotifyData = :channelNotifyData where id = :id and status = :status")
    @Override
    int updateRefundSuccess(Long id, PayRefundStatus status, LocalDateTime successTime, String channelRefundNo, PayRefundStatus updateStatus, String channelNotifyData);
}